package com.ww.springboot.boot.thread.study.base.lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntConsumer;

/**
 * 编写一个可以从 1 到 n 输出代表这个数字的字符串的程序，但是：
 * <p>
 * 如果这个数字可以被 3 整除，输出 "fizz"。
 * 如果这个数字可以被 5 整除，输出 "buzz"。
 * 如果这个数字可以同时被 3 和 5 整除，输出 "fizzbuzz"。
 * 例如，当 n = 15，输出： 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz。
 *
 *
 *
 * 四把锁 按照顺序1 2 3 4执行
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/fizz-buzz-multithreaded
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 */
class FizzBuzz {
    private int n;
    private int i = 1;

    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
    Condition condition4 = lock.newCondition();

    public FizzBuzz(int n) {
        this.n = n;
    }

    // printFizz.run() outputs "fizz".
    public void fizz(Runnable printFizz) throws InterruptedException {
        new Thread(() -> {
            while (i <= n) {
                //System.out.println("3");
                lock.lock();
                try {
                    //System.out.println("-------3----1----");
                    condition3.await();
                    //System.out.println("-------3----2----");
                    if (i % 3 == 0) {
                        printFizz.run();
                        i++;
                        condition1.signal();
                    } else {
                        condition4.signal();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }).start();
    }

    // printBuzz.run() outputs "buzz".
    public void buzz(Runnable printBuzz) throws InterruptedException {
        new Thread(() -> {
            while (i <= n) {
                //System.out.println("2");
                lock.lock();
                try {
                    //System.out.println("-------2----1----");
                    try {
                        condition2.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // System.out.println("-------2----1----");
                    if (i % 5 == 0) {
                        printBuzz.run();
                        i++;
                        condition1.signal();
                    } else {
                        condition3.signal();
                    }
                } finally {
                    lock.unlock();
                }
            }
        }).start();

    }

    // printFizzBuzz.run() outputs "fizzbuzz".
    public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
        new Thread(() -> {
            while (i <= n) {
                //System.out.println("1");
                lock.lock();
                try {
                    //System.out.println("-------1----1----");
                    if (i != 1) {
                        try {
                            condition1.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (i % 5 == 0 && i % 3 == 0) {
                        printFizzBuzz.run();
                        i++;
                    } else {
                        condition2.signal();
                    }
                    //System.out.println("-------1----2----");
                } finally {
                    lock.unlock();
                }
            }
        }).start();

    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void number(IntConsumer printNumber) throws InterruptedException {
        new Thread(() -> {
            while (i <= n) {
                //System.out.println("4");
                lock.lock();
                try {
                    // System.out.println("-------4----1----");
                    try {
                        condition4.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    printNumber.accept(i);
                    i++;
                    condition1.signal();
                    //System.out.println("-------4----2----");
                } finally {
                    lock.unlock();
                }
            }
        }).start();
    }

    public static void main(String[] args) throws InterruptedException {
        FizzBuzz fizzBuzz = new FizzBuzz(15);
        fizzBuzz.buzz(new PrintFizz());
        fizzBuzz.fizz(new PrintBuzz());
        fizzBuzz.fizzbuzz(new Fizzbuzz());
        fizzBuzz.number(value -> System.out.println(value));
    }


    static class PrintFizz implements Runnable {
        @Override
        public void run() {
            System.out.println("fizz");
        }
    }

    static class PrintBuzz implements Runnable {
        @Override
        public void run() {
            System.out.println("buzz");
        }
    }

    static class Fizzbuzz implements Runnable {
        @Override
        public void run() {
            System.out.println("fizzbuzz");
        }
    }
}